package cn.sccl.common.dao.impl;

import cn.sccl.common.model.BaseModel;
import cn.sccl.common.model.Page;
import cn.sccl.common.model.PageQueryData;
import cn.sccl.common.model.PageResult;
import cn.sccl.common.util.DataBaseUtils;
import cn.sccl.common.util.DevLog;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;

/**
 * Created by 杨浩宇 on 2016-02-13.
 */
@Repository
public class MyBatisGenericDao<T> extends SqlSessionDaoSupport {

    public static final String POSTFIX_INSERT = ".insert";
    public static final String POSTFIX_UPDATE = ".update";
    public static final String POSTFIX_DELETE = ".delete";
    public static final String POSTFIX_DELETE_PRIAMARYKEY = ".deleteByPrimaryKey";
    public static final String POSTFIX_SELECT = ".select";
    public static final String POSTFIX_SELECTMAP = ".selectByMap";
    public static final String POSTFIX_COUNT = ".count";
    public static final String POSTFIX_QUERY = ".query";
    /**
     * 插入设置主键对象
     */
    public static final String POSTFIX_INSERT_WITHKEY = ".insertWithObjectKey";
    /**
     * 逻辑批量删除
     */
    public static final String POSTFIX_LOGIC_DELETE = ".logicDelete";
    /**
     * 依据主键逻辑删除
     */
    public static final String POSTFIX_LOGIC_DELETE_PRIAMARYKEY = ".logicDeleteByPrimaryKey";
    /**
     * 更新部分字段
     */
    public static final String POSTFIX_UPDATE_PARTIAL_MODEL = ".updateMapModel";
    private static final int METADATA_PK_INDEX = 4;

    @Override
    @Autowired
    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        super.setSqlSessionTemplate(sqlSessionTemplate);
    }

    /**
     * 获取Mybatis的SQL命名空间
     * 默认把mapper.xml放在和dao相同的目录
     *
     * @return
     */
    protected String getSqlMapNamespace() {
        Package pkg = this.getClass().getPackage();
        String pkgName = pkg.getName();
        if (pkgName.endsWith(".impl")) {
            return pkgName.substring(0, pkgName.indexOf(".impl"));
        } else {
            throw new RuntimeException("无法获取sql的命名空间");
        }
    }


    /**
     * 根据ID获取实体对象
     *
     * @param entityClass
     * @param id
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T get(Class<T> entityClass, Serializable id) {

        T objDB = (T) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_SELECT, id);

        return objDB;
    }


    /**
     * 获取实体的全部记录
     *
     * @param entityClass
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> getAll(Class<T> entityClass) {
        return getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_SELECT);
    }

    /**
     * 根据参数查询实体对象列表
     *
     * @param entityClass
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> find(Class<T> entityClass, Map<String, Object> map) {
        if (map == null) {
            return getAll(entityClass);
        }
        DevLog.trace(map);
        return this.getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_SELECT, map);
    }

    /**
     * 根据条件查询唯一的一条记录
     *
     * @param entityClass
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T findUniqueBy(Class<T> entityClass, Map<String, Object> map) {
        try {
            DevLog.trace(map);
            return (T) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_SELECT, map);
        } catch (Exception e) {
            logger.error("Error when propertie on entity," + e.getMessage(), e.getCause());
            throw new RuntimeException(e);
        }
    }


    public boolean isNotUnique(Object entity, String TABLE_NAME, String names) {
        try {
            String primarykey;
            Connection con = getSqlSession().getConnection();
            ResultSet dbMetaData = con.getMetaData().getPrimaryKeys(con.getCatalog(), null, TABLE_NAME);
            dbMetaData.next();
            if (dbMetaData.getRow() > 0) {
                primarykey = dbMetaData.getString(METADATA_PK_INDEX);
                if (names.indexOf(primarykey) > -1)
                    return false;
            } else {
                return true;
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
            return false;
        }
        return false;
    }

    /**
     * 分页查询
     *
     * @param entityClass
     * @param pageIndex   页码
     * @param pageSize    分页大小
     * @param <T>
     * @return
     */
    public <T> PageResult<T> pageQuery(Class<T> entityClass, long pageIndex, int pageSize) {
        PageQueryData pageQueryData = new PageQueryData(pageIndex, pageSize);
        Page<T> pageData = (Page<T>) this.find(entityClass, pageQueryData);
        return new PageResult<T>(pageData);
    }

    /**
     * 分页查询
     *
     * @param entityClass
     * @param pageIndex   页码
     * @param pageSize    分页大小
     * @param params      查询参数
     * @param <T>
     * @return
     */
    public <T> PageResult<T> pageQuery(Class<T> entityClass, long pageIndex, int pageSize, Map<String, Object> params) {
        PageQueryData pageQueryData = new PageQueryData(pageIndex, pageSize);
        pageQueryData.putAll(params);
        Page<T> pageData = (Page<T>) this.find(entityClass, pageQueryData);
        return new PageResult<T>(pageData);
    }


    /**
     * 兼容MSS的分页查询
     *
     * @param entityClass
     * @param query
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    @Deprecated
    public <T> List<T> query(Class<T> entityClass, BaseQuery query) {
        List<T> list = new ArrayList<T>();
        Integer num = null;

        num = (Integer) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_COUNT, query);

        if (num != null && num.intValue() != 0) {
            query.setTotal(num);
            list = getSqlSession().selectList(getSqlMapNamespace() + POSTFIX_QUERY, query);
        }
        return list;
    }

    /**
     * 返回Map
     *
     * @param entityClass
     * @param key         作为返回Map对象的key值的属性
     * @param map
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> findMapBy(Class<T> entityClass, String key, Map<String, Object> map) {
        DevLog.trace(map);
        return this.getSqlSession().selectMap(getSqlMapNamespace() + POSTFIX_SELECTMAP, map, key);
    }


    /**
     * @param <T>
     * @param entityClass
     * @param ids
     * @return
     * @Title: gets
     * @Description: 按照主键 对象的形式返回
     * @createdBy:raoifeng
     * @createaAt:2014-6-23下午10:20:26
     */
    public <T> List<T> gets(Class<T> entityClass, List<Long> ids) {
        HashMap<String, T> returnMap = new HashMap<String, T>(ids.size());
        for (Serializable id : ids) {
            T objDB = (T) getSqlSession().selectOne(getSqlMapNamespace() + POSTFIX_SELECT, id);
            if (objDB != null) {
                returnMap.put(id.toString(), objDB);
            }
        }
        List<T> listValue = new ArrayList<T>();
        Iterator it = returnMap.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next().toString();
            listValue.add(returnMap.get(key));
        }
         return listValue;
    }

    /**
     * 新增数据
     * 数据ID由Mybatis生成
     *
     * @param o
     */
    public void insert(Object o) {
        //考虑到数据库分区信息year新增的时候从数据库生成，缓存后因某些业务需要，故取服务器的年份
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        ((BaseModel) o).setYear(year);
        ((BaseModel) o).setCreateTime(new Timestamp(System.currentTimeMillis()));

        getSqlSession().insert(getSqlMapNamespace() + POSTFIX_INSERT, o);
    }

    /**
     * 新增数据
     * 数据ID由用户定义
     *
     * @param o
     */
    public void insertWithPrimaryKey(Object o) {
        //考虑到数据库分区信息year新增的时候从数据库生成，缓存后因某些业务需要，故取服务器的年份
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        ((BaseModel) o).setYear(year);
        ((BaseModel) o).setCreateTime(new Timestamp(System.currentTimeMillis()));


        getSqlSession().insert(getSqlMapNamespace() + POSTFIX_INSERT_WITHKEY, o);

    }

    /**
     * 更新数据
     * 对象全字段更新
     * 不会立即更新缓存
     *
     * @param o
     */
    public void update(Object o) {
        this.update(o, Boolean.FALSE);
    }


    /**
     * 更新数据
     * 对象全字段更新
     *
     * @param o
     * @param isupdateMC 是否立即更新缓存
     */
    public void update(Object o, boolean isupdateMC) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_UPDATE, o);

    }

    /**
     * 更新数据
     * 更新部分字段
     *
     * @param o 需要保存的对象，如果属性为null，则不更新对应的字段
     */
    public void updatePartialModel(Object o) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_UPDATE_PARTIAL_MODEL, DataBaseUtils.Model2HashMap(o));

    }

    /**
     * 更新数据
     * 更新部分字段
     *
     * @param o             需要更新的对象
     * @param aryNullFields 需要保持为null值的属性数组
     */
    public void updatePartialModel(Object o, String[] aryNullFields) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_UPDATE_PARTIAL_MODEL, DataBaseUtils.Model2HashMap(o, aryNullFields));

    }

    /**
     * 批量删除
     * 删除所有与对象中不为null的属性的记录
     *
     * @param o
     */
    @Deprecated
    public void remove(Object o) {
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_LOGIC_DELETE, o);

    }


    /**
     * 根据ID删除数据
     *
     * @param entityClass
     * @param id
     * @param <T>
     */
    public <T extends BaseModel> void removeById(Class<T> entityClass, Serializable id) {
        if (id == null) {
            throw new RuntimeException("id must not be null");
        }
        T obj = null;
        try {
            obj = entityClass.newInstance();
            obj.setId((String) id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        getSqlSession().update(getSqlMapNamespace() + POSTFIX_LOGIC_DELETE_PRIAMARYKEY, id);

    }

}
